﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using System.Linq;

namespace Gmantis.Controls
{
    internal class CommandToElementsMap
    {
        // Fields
        private readonly Dictionary<string, List<WeakReference>> _deferredCommands = new Dictionary<string, List<WeakReference>>();
        private Dispatcher _dispatcher;
        private readonly Dictionary<WeakReference, List<WeakReference>> _map = new Dictionary<WeakReference, List<WeakReference>>();
        private bool _needPrunning = true;

        // Methods
        internal void AddDeferredCommand(FrameworkElement element, string cmd)
        {
            if (!this._deferredCommands.ContainsKey(cmd))
            {
                this._deferredCommands.Add(cmd, new List<WeakReference>());
            }
            this._deferredCommands[cmd].Add(new WeakReference(element));
        }

        internal void AddElementToMap(FrameworkElement element, ICommand cmd)
        {
            List<WeakReference> elementsFromCommand = this.GetElementsFromCommand(cmd);
            if (elementsFromCommand == null)
            {
                elementsFromCommand = new List<WeakReference>();
                this._map.Add(new WeakReference(cmd), elementsFromCommand);
            }
            elementsFromCommand.Add(new WeakReference(element));
        }

        internal void ForEachElement(ICommand cmd, Action<FrameworkElement> callback)
        {
            List<WeakReference> elementsFromCommand = this.GetElementsFromCommand(cmd);
            for (int i = elementsFromCommand.Count - 1; i > -1; i--)
            {
                FrameworkElement target = elementsFromCommand[i].Target as FrameworkElement;
                if (target != null)
                {
                    callback(target);
                }
            }
        }

        private List<WeakReference> GetElementsFromCommand(ICommand cmd)
        {
            this.Prune();
            return this._map.FirstOrDefault<KeyValuePair<WeakReference, List<WeakReference>>>(entry => (entry.Key.Target == cmd)).Value;
        }

        private void Prune()
        {
            Action a = null;
            if ((this._dispatcher == null) && (Deployment.Current != null))
            {
                this._dispatcher = Deployment.Current.Dispatcher;
            }
            if (this._dispatcher != null)
            {
                this._needPrunning = true;
                if (a == null)
                {
                    a = delegate
                    {
                        if (this._needPrunning)
                        {
                            this._needPrunning = false;
                            List<WeakReference> list = this._map.Keys.ToList<WeakReference>();
                            for (int n = list.Count - 1; n > -1; n--)
                            {
                                WeakReference key = list[n];
                                if (!key.IsAlive)
                                {
                                    this._map.Remove(key);
                                }
                                else
                                {
                                    List<WeakReference> list2 = this._map[key];
                                    for (int i = list2.Count - 1; i > -1; i--)
                                    {
                                        if (!list2[i].IsAlive)
                                        {
                                            list2.RemoveAt(i);
                                        }
                                    }
                                }
                            }
                            if (this._deferredCommands.Count > 0)
                            {
                                List<string> list3 = this._deferredCommands.Keys.ToList<string>();
                                for (int j = list3.Count - 1; j > -1; j--)
                                {
                                    string str = list3[j];
                                    List<WeakReference> list4 = this._deferredCommands[str];
                                    for (int k = list4.Count - 1; k > -1; k--)
                                    {
                                        if (!list4[k].IsAlive)
                                        {
                                            list4.RemoveAt(k);
                                        }
                                    }
                                    if (list4.Count == 0)
                                    {
                                        this._deferredCommands.Remove(str);
                                    }
                                }
                            }
                        }
                    };
                }
                this._dispatcher.BeginInvoke(a);
            }
        }

        internal void RemoveElementFromMap(FrameworkElement element, ICommand cmd)
        {
            List<WeakReference> elementsFromCommand = this.GetElementsFromCommand(cmd);
            if (elementsFromCommand != null)
            {
                for (int i = elementsFromCommand.Count - 1; i > -1; i--)
                {
                    if (elementsFromCommand[i].Target == element)
                    {
                        elementsFromCommand.RemoveAt(i);
                        return;
                    }
                }
            }
        }

        internal void ResolveDeferredCommands()
        {
            if (this._deferredCommands.Count != 0)
            {
                List<string> list = this._deferredCommands.Keys.ToList<string>();
                for (int i = list.Count - 1; i > -1; i--)
                {
                    string cmd = list[i];
                    ICommand command = CommandExtensions.TryResolveCommand(cmd);
                    if (command != null)
                    {
                        List<WeakReference> list2 = this._deferredCommands[cmd];
                        for (int j = list2.Count - 1; j > -1; j--)
                        {
                            FrameworkElement target = list2[j].Target as FrameworkElement;
                            if (target != null)
                            {
                                CommandExtensions.SetCommand(target, command);
                            }
                        }
                        this._deferredCommands.Remove(cmd);
                    }
                }
            }
        }

        // Properties
        internal List<ICommand> Commands
        {
            get
            {
                List<ICommand> list = new List<ICommand>();
                List<WeakReference> list2 = this._map.Keys.ToList<WeakReference>();
                for (int i = list2.Count - 1; i > -1; i--)
                {
                    ICommand target = list2[i].Target as ICommand;
                    if (target != null)
                    {
                        list.Add(target);
                    }
                }
                return list;
            }
        }
    }
}
